package main

import (
	"context"
	"fmt"
	"github.com/getlantern/systray"
	"github.com/shopspring/decimal"
	"runtime"
	"sort"
	"stock/server/dao"
	"stock/server/internal"
	"stock/server/internal/pkg/notify"
	"stock/server/internal/pkg/stock/typeutil"
	"time"
	"unicode"
)

var (
	systraySvr *systrayService // 默认服务
	// nilMenuHandler = func(_ *systray.MenuItem, _ *systrayService, _ ...any) {} // 空函数
)

// 菜单节点
type menuNode struct {
	stockInfo dao.StockInfo
	item      *systray.MenuItem
	parent    *menuNode
	child     []*menuNode
}

type systrayService struct {
	ctx      context.Context
	cancelFn context.CancelFunc

	stock                *internal.Stock
	menuMap              map[string]*menuNode
	menuArr              []*systray.MenuItem
	isSystrayListen      bool
	systrayListenOrderBy bool
}

func Init() {
	systraySvr = &systrayService{
		menuMap: make(map[string]*menuNode),
	}

	go func() {
		for {
			time.Sleep(time.Second * 3)
			fmt.Println(runtime.NumGoroutine())
		}
	}()

	systray.Run(systraySvr.onReady, nil)
}

func (o *systrayService) onReady() {
	systray.SetIcon(iconBytes)

	o.stock = internal.NewStock()

	o.ctx, o.cancelFn = context.WithCancel(context.Background())
	o.stock.StartUp(o.ctx)

	o.menuInit()

	_ = notify.MacNotify{}.Push("系统盯盘已开启...", "")
}

func (o *systrayService) menuInit() {
	// 菜单
	o.addMenuHandle(systray.AddMenuItem("刷新", "开启/关闭托盘盯盘"), func(item *systray.MenuItem, service *systrayService, a ...any) {
		o.cancelFn() // 取消所有进程
	})

	// 默认是开启,可以在「设置 > 托盘盯盘」下关闭
	stockMenu := systray.AddMenuItem("盯盘", "")
	o.menuMap["tray_watch"] = &menuNode{
		item: stockMenu,
	}

	settingMenu := systray.AddMenuItem("设置", "设置")

	// 托盘盯盘 > start
	systrayListen := settingMenu.AddSubMenuItem("托盘盯盘", "")

	closeSystrayListen := systrayListen.AddSubMenuItem("关闭", "")
	openSystrayListen := systrayListen.AddSubMenuItem("开启", "")
	openSystrayListen.Hide() // 默认隐藏「设置 > 托盘管理 > 开启」

	// 渲染托盘盯盘
	o.renderTrayMenu()

	o.addMenuHandle(closeSystrayListen, func(item *systray.MenuItem, service *systrayService, a ...any) {
		item.Hide()                     // 隐藏「设置 > 托盘管理 > 关闭」
		a[0].(*systray.MenuItem).Hide() // 隐藏「盯盘」
		a[1].(*systray.MenuItem).Show() // 显示「设置 > 托盘管理 > 开启」
	}, stockMenu, openSystrayListen)

	o.addMenuHandle(openSystrayListen, func(item *systray.MenuItem, service *systrayService, a ...any) {
		item.Hide()                     // 隐藏「设置 > 托盘管理 > 开启」
		a[0].(*systray.MenuItem).Show() // 显示「盯盘」
		a[1].(*systray.MenuItem).Show() // 显示「设置 > 托盘管理 > 关闭」

	}, stockMenu, closeSystrayListen)

	o.addMenuHandle(systrayListen.AddSubMenuItem("排序·升序", ""), func(item *systray.MenuItem, service *systrayService, a ...any) {
		if o.systrayListenOrderBy {
			item.SetTitle("排序·升序")
		} else {
			item.SetTitle("排序·倒序")
		}
		o.systrayListenOrderBy = !o.systrayListenOrderBy

		// 重新渲染
		o.renderTrayMenu()
	})
	// 托盘盯盘 > end

	// 退出
	o.addMenuHandle(systray.AddMenuItem("退出", "退出App"), systrayExitHandler)

}

func (o *systrayService) addMenuHandle(item *systray.MenuItem, handler func(*systray.MenuItem, *systrayService, ...any), args ...any) {
	go func() {
		for {
			select {
			case <-item.ClickedCh:
				handler(item, o, args...)
			}
		}
	}()
}

// 退出app
func systrayExitHandler(_ *systray.MenuItem, _ *systrayService, _ ...any) {
	systray.Quit()
}

// 渲染托盘盯盘
func (o *systrayService) renderTrayMenu() {
	rootNode := o.menuMap["tray_watch"]

	// 获取分组
	groupIdx, groupMap, _ := o.stock.DB.Group()

	// 状态栏
	var statusBarCodeIds []string
	var statusBarChannel = make(chan struct{})

	go func() {
		for {
			var i int
			for _, groupName := range groupIdx {
				var node *menuNode
				if len(rootNode.child) == 0 || len(rootNode.child)-1 < i {
					node = &menuNode{
						item:   rootNode.item.AddSubMenuItem(groupName, ""),
						parent: rootNode,
					}
					rootNode.child = append(rootNode.child, node)
				} else {
					node = rootNode.child[i]
					node.item.SetTitle(groupName)
					node.item.Show()
				}

				if i == len(groupIdx)-1 {
					for j := i + 1; j < len(rootNode.child); j++ {
						rootNode.child[j].item.Hide()
					}
				}
				i++

				// 渲染子菜单
				var childList []*menuNode

				var nowKlines []typeutil.Kline
				for _, codeId := range groupMap[groupName] {
					o.stock.Mux.Lock()
					klines := o.stock.KlineMap[internal.Realtime][codeId]
					o.stock.Mux.Unlock()
					if klines == nil {
						continue
					}
					lastKline := klines[len(klines)-1]
					nowKlines = append(nowKlines, lastKline)
				}

				// 进行对齐
				var codeIds, codeNames, closeValues, increasePercentageValues []string

				// 自定义排序
				sort.Slice(nowKlines, func(i, j int) bool {
					if o.systrayListenOrderBy { // true 升序, false 倒序
						return nowKlines[i].IncreasePercentage < nowKlines[j].IncreasePercentage
					}
					return nowKlines[i].IncreasePercentage > nowKlines[j].IncreasePercentage
				})

				for _, kline := range nowKlines {
					codeIds = append(codeNames, o.stock.CodeInfoMap[kline.Symbol].Code)
					codeNames = append(codeNames, o.stock.CodeInfoMap[kline.Symbol].Abbreviation)

					o.stock.Mux.Lock()
					klines := o.stock.KlineMap[internal.Realtime][kline.Symbol]
					o.stock.Mux.Unlock()
					if klines == nil {
						continue
					}
					lastKline := klines[len(klines)-1]
					closeValues = append(closeValues, decimal.NewFromFloatWithExponent(lastKline.ClosePrice, -3).String())
					increasePercentageValues = append(increasePercentageValues, decimal.NewFromFloatWithExponent(lastKline.IncreasePercentage, -2).String())
				}

				codeNames = o.calculateAlignment(codeNames, false)
				closeValues = o.calculateAlignment(closeValues, true)
				increasePercentageValues = o.calculateAlignment(increasePercentageValues, true)

				for idx, codeName := range codeNames {
					if len(closeValues) == 0 {
						continue
					}
					if len(node.child) == 0 || len(node.child)-1 < idx {
						menuItem := &menuNode{
							stockInfo: o.stock.CodeInfoMap[nowKlines[idx].Symbol],
							item:      node.item.AddSubMenuItem(fmt.Sprintf("%s\t|  %s\t|  %s", codeName, closeValues[idx], increasePercentageValues[idx]), codeIds[idx]),
							parent:    node,
						}
						childList = append(childList, menuItem)
						go func(item *menuNode) {
							for {
								select {
								case <-item.item.ClickedCh:
									var isExist bool
									// 存在就删除
									for statusIdx, codeId := range statusBarCodeIds {
										if codeId == item.stockInfo.Code {
											isExist = true
											statusBarCodeIds = append(statusBarCodeIds[:statusIdx], statusBarCodeIds[statusIdx+1:]...)
											break
										}
									}

									if isExist {
										statusBarChannel <- struct{}{}
										break
									}
									if len(statusBarCodeIds) >= 3 {
										statusBarCodeIds = statusBarCodeIds[1:]
									}
									statusBarCodeIds = append(statusBarCodeIds, item.stockInfo.Code)
									statusBarChannel <- struct{}{}
								}
							}
						}(menuItem)
					} else {
						node.child[idx].item.SetTitle(fmt.Sprintf("%s\t|  %s\t|  %s", codeName, closeValues[idx], increasePercentageValues[idx]))
						node.child[idx].item.SetTooltip(codeIds[idx])
						node.child[idx].item.Show()
						node.child[idx].stockInfo = o.stock.CodeInfoMap[nowKlines[idx].Symbol]
						childList = append(childList, node.child[idx])
					}

					if idx == len(codeNames)-1 {
						for j := idx + 1; j < len(node.child); j++ {
							node.child[j].item.Hide()
						}
					}
				}

				node.child = childList
			}

			// 通知状态栏
			statusBarChannel <- struct{}{}

			// <- typeutil.Kline
			o.stock.RealtimeListen()
		}
	}()

	// 渲染状态栏
	go func() {
		for {
			select {
			case <-statusBarChannel:
				var s string
				for _, codeId := range statusBarCodeIds {
					o.stock.Mux.Lock()
					klines := o.stock.KlineMap[internal.Realtime][codeId]
					o.stock.Mux.Unlock()
					if klines == nil {
						continue
					}
					lastKline := klines[len(klines)-1]
					s += " " + fmt.Sprintf("%s %s(%s)", o.stock.CodeInfoMap[codeId].Abbreviation, decimal.NewFromFloatWithExponent(lastKline.ClosePrice, -3), decimal.NewFromFloatWithExponent(lastKline.IncreasePercentage, -2))
				}
				systray.SetTitle(s)
			}
		}
	}()
}

// 关闭盯盘菜单
func (o *systrayService) closeTrayWatchMenu() {
	// 递归关闭
	var closeFn func(node *menuNode)
	closeFn = func(node *menuNode) {
		// 关闭自身
		node.item.Hide()
		if len(node.child) == 0 {
			return
		}

		for _, item := range node.child {
			closeFn(item)
		}
	}

	closeFn(o.menuMap["tray_watch"])
}

func (o *systrayService) calculateAlignment(strArr []string, isDigit bool) []string {
	// 找到数组中最长的字符串长度
	maxLength := 0
	var lens []int
	for _, str := range strArr {
		var length int
		for _, r := range str {
			length++
			if !isDigit && (unicode.IsLetter(r) || unicode.IsDigit(r)) {
				length++
			}
		}
		if length > maxLength {
			maxLength = length
		}
		lens = append(lens, length)
	}

	// 使用空格来补齐字符串长度
	paddedArr := make([]string, len(strArr))
	for i, str := range strArr {
		if lens[i] < maxLength {
			if maxLength-len(str) >= 4 {
				str += "\t"
			} else {
				for len(str) < maxLength {
					str += " "
				}
			}
		}

		paddedArr[i] = str
	}

	return paddedArr
}
